package ray.surface;

import ray.math.Point3;
import ray.math.Vector3;
import ray.*;

/**
 * Represents a sphere as a center and a radius.
 *
 * @author ags
 */
public class Sphere extends Surface {
	
	/** The center of the sphere. */
	protected final Point3 center = new Point3();
	public void setCenter(Point3 center) { this.center.set(center); }
	
	/** The radius of the sphere. */
	protected double radius = 1.0;
	public void setRadius(double radius) { this.radius = radius; }
	
	public Sphere() { }
	
	public boolean intersect(Intersection inter, Ray ray) {
		
		Vector3 sphCenter = new Vector3(center);
		Vector3 sphNorm = new Vector3(ray.origin);
		sphNorm.sub(sphCenter);
		Vector3 dirneg = new Vector3(ray.direction);
		dirneg.scale(-1);
		
		double b = dirneg.dot(sphNorm);
		double c = Math.pow(ray.direction.dot(sphNorm), 2) - ray.direction.dot(ray.direction) * (sphNorm.dot(sphNorm) - (radius*radius));
		
		if (c < 0)
			return false;
		
		double tin = (b + Math.sqrt(c)) / (ray.direction.dot(ray.direction));
		double tout = (b - Math.sqrt(c)) / (ray.direction.dot(ray.direction));
		
		Point3 intersect1 = new Point3();
		ray.getPoint(intersect1, tin);
		
		Point3 intersect2 = new Point3();
		ray.getPoint(intersect2, tout);
		
		Point3 actualPoint = new Point3();
		double actualT = 0;
		
		//check to see which is actual entry point (closer to origin) to set as intersection
		if (ray.origin.distance(intersect1) < ray.origin.distance(intersect2))
			{ actualPoint = intersect1; actualT = tin; }
		else if (ray.origin.distance(intersect1) > ray.origin.distance(intersect2))
			{ actualPoint = intersect2; actualT = tout; }
		else
			{ actualPoint.set(intersect1); actualT = tin; }
		
		if (actualT >= ray.tEnd || actualT < ray.tStart)
			return false;
		
		inter.intersection.set(actualPoint);
		inter.normal.sub(actualPoint, center);
		inter.surface = this;
		if(inter.t > actualT) {
			inter.t = actualT;
			}
		return true;
	}
	
	// to get normal of sphere take point - center
	
	/**
	 * @see Object#toString()
	 */
	public String toString() {
		
		return "sphere " + center + " " + radius + " " + shader + " end";
	}
	
}
